package jwtauth

import (
	"cubeAdmin/common"
	"cubeAdmin/common/redis"
	"cubeAdmin/models"
	"cubeAdmin/vo"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/beego/beego/v2/core/logs"
	beego "github.com/beego/beego/v2/server/web"
	"github.com/beego/beego/v2/server/web/context"
	"github.com/dgrijalva/jwt-go"
	"strconv"
	"strings"
	"time"
)

type userStdClaims struct {
	vo.JwtUser
	//*models.User
	jwt.StandardClaims
}

var (
	verifyKey  string
	ErrAbsent  = "token absent"  // 令牌不存在
	ErrInvalid = "token invalid" // 令牌无效
	ErrExpired = "token expired" // 令牌过期
	ErrOther   = "other error"   // 其他错误
)

const bearerLength = len("Bearer ")

func init() {
	verifyKey, _ = beego.AppConfig.String("jwt_token")
}

func GenerateToken(m *models.SysUser, d time.Duration) (string, error) {
	m.Password = ""
	//m.Permissions = []string{}
	expireTime := time.Now().Add(d)
	stdClaims := jwt.StandardClaims{
		ExpiresAt: expireTime.Unix(),
		Id:        strconv.FormatInt(m.Id, 10),
		Issuer:    "cubeAdmin",
	}

	var jwtUser = vo.JwtUser{
		Id:       m.Id,
		Avatar:   m.Avatar,
		Email:    m.Email,
		Username: m.Username,
		Phone:    m.Phone,
		NickName: m.NickName,
		Sex:      m.Sex,
		Dept:     m.Depts.Name,
		Job:      m.Jobs.Name,
	}

	uClaims := userStdClaims{
		StandardClaims: stdClaims,
		JwtUser:        jwtUser,
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, uClaims)
	tokenString, err := token.SignedString([]byte(verifyKey))
	if err != nil {
		logs.Info(err)
	}
	//set redis
	var key = common.RedisPrefixAuth + tokenString
	json, _ := json.Marshal(m)
	err = redis.Rd.SetEx(key, string(json), expireTime.Unix())
	return tokenString, err
}

func ValidateToken(tokenString string) (*vo.JwtUser, error) {
	if tokenString == "" {
		return nil, errors.New(ErrAbsent)
	}
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return []byte(verifyKey), nil
	})
	if token == nil {
		return nil, errors.New(ErrInvalid)
	}
	claims := userStdClaims{}
	_, err = jwt.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return []byte(verifyKey), nil
	})
	if err != nil {
		return nil, err
	}
	return &claims.JwtUser, err
}

// GetAdminUserId 返回登录用户Id
func GetAdminUserId(c *context.BeegoInput) (int64, error) {
	u := c.GetData(common.ContextKeyUserObj)
	user, ok := u.(*vo.JwtUser)
	if ok {
		return user.Id, nil
	}
	return 0, errors.New("can't convert to user struct")
}

// GetAdminUser 返回user
func GetAdminUser(c *context.BeegoInput) (*vo.JwtUser, error) {
	u := c.GetData(common.ContextKeyUserObj)
	user, ok := u.(*vo.JwtUser)
	if ok {
		return user, nil
	}
	return nil, errors.New("can't convert to user struct")
}

// GetAdminDetailUser 返回 detail user
func GetAdminDetailUser(c *context.BeegoInput) (*models.SysUser, error) {
	mytoken := c.Cookie("token")
	if mytoken != "" {
		token := strings.TrimSpace(mytoken[bearerLength:])
		var key = common.RedisPrefixAuth + token
		userMap, _ := redis.Rd.Get(key)
		user := &models.SysUser{}
		err := json.Unmarshal([]byte(userMap), user)
		return user, err
	} else {
		return nil, errors.New("token cannot be empty")
	}
}

// GetToken 获取Token
func GetToken(c *context.BeegoInput) string {
	myToken := c.Cookie("token")
	return myToken
}

// UpdateSession 更新session
func UpdateSession(c *context.BeegoInput) error {
	token := c.Cookie("token")
	if token != "" {
		token := strings.TrimSpace(token[bearerLength:])
		var key = common.RedisPrefixAuth + token
		userMap, _ := redis.Rd.Get(key)
		user := &models.SysUser{}
		err := json.Unmarshal([]byte(userMap), user)
		m, err := models.GetUserById(user.Id)
		marshal, _ := json.Marshal(m)
		err = redis.Rd.SetEx(key, string(marshal), int64(time.Minute*30))
		return err
	} else {
		return errors.New("token cannot be empty")
	}
}

// RemoveUser 删除缓存
func RemoveUser(c *context.BeegoInput) error {
	//mytoken := c.Header("Authorization")
	mytoken := c.Cookie("token")
	token := strings.TrimSpace(mytoken[bearerLength:])
	var key = common.RedisPrefixAuth + token
	return redis.Rd.Del(key)
}
